#include "web-socket.h"

void unixerror(char *msg) {
	fprintf(stderr, "%s: %s", msg, strerror(errno));
	// exit(0);
}

int openListenfd(int port) {
	int listenfd, optval = 1;
	struct sockaddr_in serveraddr;

	if ((listenfd = socket(AF_INET, SOCK_STREAM ,0)) < 0) 
		return -1;
	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(int)) < 0)
		return -1;
	memset((char *)&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons((unsigned short)port);

	if (bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) 
		return -1;
	if (listen(listenfd, LISTENQ) < 0) 
		return -1;
	return listenfd;
}

header *initheader(int fd) {
	int rc;
	header *header;
	char *head;

	head = malloc(sizeof(char)*MAXBUF);
	rc = read(fd, head, MAXBUF);
	if (rc <= 0)
		unixerror("Read error");
	header = malloc(sizeof(*header));
	header->cfd = fd;
	header->widx = rc;
	header->ridx = 0;
	header->limit = MAXBUF;
	header->head = head;
	return header;
}

void closeClient(header *header) {
	int rc = close(header->cfd); 
	if (rc < 0)
		unixerror("Close client error");
	free(header->head);
	free(header);
}

int readline(header *header, char *buf) {
	int n, i;

	for (n = 0; n < header->limit; n++) {
		if (header->ridx == header->widx) {
			if (header->widx == header->limit) {
				// 扩容
				int limit = header->limit * 2;
				char *head = malloc(sizeof(char)*limit);
				for (i = 0; i < header->limit; i++) 
					head[i] = header->head[i];
				header->limit = limit;
				free(header->head);
				header->head = head;
			}
			char *buf = header->head + header->widx;
			int rc = read(header->cfd, buf, header->limit - header->widx);
			header->widx += rc;
		}
		buf[n] = header->head[header->ridx++];
		if (buf[n] == '\n') {
			buf[++n] = '\0';
			return n;
		}
	}
	return 0;
}

int writen(int fd, void *buf, int count) {
    int nwritten, totlen = 0;

    while(totlen != count) {
        nwritten = write(fd,buf,count-totlen);
        if (nwritten == 0) return totlen;
        if (nwritten == -1) return -1;
        totlen += nwritten;
        buf += nwritten;
    }
    return totlen;
}

void processRequest(int fd) {
	struct stat sbuf;
    char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
    char filename[MAXLINE];

	header *header = initheader(fd);
	readline(header, buf);
	printf("Request headers\n");
	printf("%s", buf);
	sscanf(buf, "%s %s %s", method, uri, version);
	if (strcasecmp(method, "GET")) {             
		clienterror(fd, method, "501", "Not Implemented",
                "Tiny does not implement this method");
        return;
    }
	parseuri(uri, filename);     
    if (stat(filename, &sbuf) < 0) {       
		clienterror(fd, filename, "404", "Not found",
			    "Tiny couldn't find this file");
		return;
    }
	if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { 
	    clienterror(fd, filename, "403", "Forbidden",
			"Tiny couldn't read the file");
	    return;
	}
	readrequesthdrs(header);
	serverstatic(fd, filename, sbuf.st_size);
	closeClient(header);
}

void parseuri(char *uri, char *filename) {
	strcpy(filename, ".");      
	strcat(filename, uri);     
	if (uri[strlen(uri)-1] == '/')       
	    strcat(filename, "index.html"); 
}

void clienterror(int fd, char *cause, char *errnum,
		 char *shortmsg, char *longmsg) {
    char buf[MAXLINE], body[2018];

    /* Build the HTTP response body */
    sprintf(body, "<html><title>Tiny Error</title>");
    sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body);
    sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
    sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
    sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);

    /* Print the HTTP response */
    sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
    writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-type: text/html\r\n");
    writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
    writen(fd, buf, strlen(buf));
    writen(fd, body, strlen(body));
}

void serverstatic(int fd, char *filename, int filesize) {
	int srcfd;
	char filetype[MAXLINE], buf[1024];

	getfiletype(filename, filetype);
	sprintf(buf, "HTTP/1.0 200 OK\r\n"); 
    sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
    sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
    sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
	writen(fd, buf, strlen(buf));

	srcfd = open(filename, O_RDONLY, 0);
	if (srcfd < 0) 
		unixerror("Open file error");
	sendfile(fd, srcfd, 0, filesize);
	int rc = close(srcfd); 
	if (rc < 0)
		unixerror("Close file error");
}

void getfiletype(char *filename, char *filetype) {
    if (strstr(filename, ".html"))
		strcpy(filetype, "text/html");
    else if (strstr(filename, ".gif"))
		strcpy(filetype, "image/gif");
    else if (strstr(filename, ".jpg"))
		strcpy(filetype, "image/jpeg");
    else
		strcpy(filetype, "text/plain");
}

void readrequesthdrs(header *header) {
    char buf[MAXLINE];

    readline(header, buf);
    while(strcmp(buf, "\r\n")) {
		readline(header, buf);
		printf("%s", buf);
    }
    return;
}
